#pragma once

#include <cstdint>
#include <type_traits>
#include <memory>
#include <array>
#include <tuple>
#include <queue>
#include <stack>

#include <boost/array.hpp>

#define BZBEE_HAS_MEMBER_FUNCTION(member) \
template<typename T, typename... Args> \
struct HasMemberFunction_##member \
{ \
private: \
    template<typename U> static auto Check(int) -> decltype(std::declval<U>().member(std::declval<Args>()...), std::true_type()); \
    template<typename U> static std::false_type Check(...); \
public: \
    enum{value = std::is_same<decltype(Check<T>(0)), std::true_type>::value}; \
};

#define BZBEE_HAS_MEMBER_TYPE(member) \
template< typename T, typename = void > \
struct HasMemberType_##member : std::false_type {}; \
template< typename T > \
struct HasMemberType_##member < T, Boyee::void_t< typename T::member > >: std::true_type {};

namespace bzbee
{
    //////////////////////////////////////////////////////////////////////////
    // 判断一个类型是否为某个模版类的实例
    // example: 
    //
    // template<typename T>
    // struct is_tuple : is_instantiation_of<std::tuple, T>
    // {
    // };
    //
    // static_assert(is_tuple<std::tuple<int, int>>::value, "not"); //ok
    // static_assert(is_tuple<Person>::value, "not"); //error

    template < template < typename... > class U, typename T >
    struct isInstantiationOf : std::false_type {};

    template < template < typename... > class U, typename... Args >
    struct isInstantiationOf< U, U<Args...> > : std::true_type {};
    //////////////////////////////////////////////////////////////////////////

    namespace detail
    {
        template< typename T >
        std::false_type static checkTupleSize(...);

        template< typename T >
        std::true_type static checkTupleSize(decltype(std::tuple_size<T>::value)*);

        template< typename T >
        struct hasTupleSize : decltype(checkTupleSize<T>(nullptr))
        {
        };

        template< typename T >
        struct hasBeginEnd
        {
        private:
            template< typename U >
            static auto Check(int)->decltype(std::declval<U>().begin(), std::declval<U>().end(), std::true_type());

            template< typename U >
            static std::false_type Check(...);

        public:
            enum
            {
                value = std::is_same< decltype(Check<T>(0)), std::true_type >::value
            };
        };

        template < typename T >
        struct hasConstIterator
        {
        private:
            template< typename C >
            static std::true_type Check(typename C::const_iterator*);

            template< typename C >
            static std::false_type Check(...);

        public:
            enum
            {
                value = std::is_same< decltype(Check<T>(0)), std::true_type >::value
            };
        };

        template < typename T >
        struct hasMappedType
        {
        private:
            template< typename C >
            static std::true_type Check(typename C::mapped_type*);

            template< typename C >
            static std::false_type Check(...);

        public:
            enum
            {
                value = std::is_same< decltype(Check<T>(0)), std::true_type >::value
            };
        };

    } // !namespace detail

    template< typename T >
    struct isString : std::integral_constant< bool, std::is_same< decay_t<T>, std::string >::value >
    {
    };

    //template < typename T >
    //struct IsContainer : std::integral_constant< bool, detail::hasConstIterator< decay_t<T> >::value
    //    && detail::hasBeginEnd< decay_t<T> >::value
    //    && !isString< decay_t<T> >::value >
    //{
    //};

    template<typename T, typename _ = void>
    struct IsContainer : std::false_type {};

    template<typename T>
    struct IsContainer<
        T,
        std::conditional_t<
        false,
        std::void_t<
        typename T::value_type,
        typename T::size_type,
        typename T::allocator_type,
        typename T::iterator,
        typename T::const_iterator,
        decltype(std::declval<T>().size()),
        decltype(std::declval<T>().begin()),
        decltype(std::declval<T>().end()),
        decltype(std::declval<T>().cbegin()),
        decltype(std::declval<T>().cend())
        >,
        void
        >
    > : public std::true_type{};

    template< typename T >
    struct isArrayImpl : std::false_type
    {
    };

    template< typename T, std::size_t N >
    struct isArrayImpl< std::array< T, N > > : std::true_type
    {
    };

    template< typename T, std::size_t N >
    struct isArrayImpl< boost::array< T, N > > : std::true_type
    {
    };

    template < typename T >
    struct IsArray : std::integral_constant< bool, std::is_array< decay_t<T> >::value
        || isArrayImpl< decay_t<T> >::value >
    {
    };

    template < typename T >
    struct IsSingleValueContainer : std::integral_constant< bool, IsContainer< decay_t<T> >::value
        && !IsArray< decay_t<T> >::value
        && !detail::hasTupleSize< decay_t<T> >::value
        && !detail::hasMappedType< decay_t<T> >::value>
    {
    };

    template < typename T >
    struct IsMapContainer : std::integral_constant< bool, IsContainer< decay_t<T> >::value
        && detail::hasMappedType< decay_t<T> >::value >
    {
    };

    template< typename T >
    struct IsNormalClass : std::integral_constant< bool, std::is_class< decay_t<T> >::value
        && !isString<T>::value >
    {
    };

    template< typename T >
    struct IsBasicType : std::integral_constant< bool, std::is_arithmetic< decay_t<T> >::value >
    {
    };

    template< typename T >
    struct isSmartPoinerImpl : std::false_type
    {
    };

    template< typename T >
    struct isSmartPoinerImpl< std::shared_ptr<T> > : std::true_type
    {
    };

    template< typename T >
    struct isSmartPoinerImpl< std::unique_ptr<T> > : std::true_type
    {
    };

    template< typename T >
    struct isSmartPoinerImpl< std::weak_ptr<T> > : std::true_type
    {
    };

    template< typename T >
    struct IsSmartPointer : isSmartPoinerImpl< decay_t<T> >
    {
    };

    template< typename T >
    struct IsPointer : std::integral_constant< bool, std::is_pointer< decay_t<T> >::value
        || IsSmartPointer< decay_t<T> >::value >
    {
    };

    template < typename T, template <typename...> class Template >
    struct isSpecializationOf : std::false_type
    {
    };

    template < template <typename...> class Template, typename... Args >
    struct isSpecializationOf< Template<Args...>, Template > : std::true_type
    {
    };

    template< typename T >
    struct IsTuple : isSpecializationOf< decay_t<T>, std::tuple >
    {
    };

    template< typename T >
    struct IsQueue : isSpecializationOf< decay_t<T>, std::queue >
    {
    };

    template< typename T >
    struct IsStack : isSpecializationOf< decay_t<T>, std::stack >
    {
    };

    template< typename T >
    struct isPriorityQueue : isSpecializationOf< decay_t<T>, std::priority_queue >
    {
    };

    template< typename T >
    struct isContainerAdapter : std::integral_constant< bool, IsQueue<T>::value
        || IsStack<T>::value
        || isPriorityQueue<T>::value >
    {
    };

    template< typename T >
    struct isUserClass : std::integral_constant< bool, IsNormalClass<T>::value
        && !isContainerAdapter<T>::value
        && !IsContainer<T>::value
        && !IsTuple<T>::value >
    {
    };

} // !namespace Boyee
